﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;
using System.Collections.Generic;

namespace BlueRoseGames.Controls.Text
{
    public enum TokenType
    {
        None,
        Command,
        Value
    }

    public class Token
    {
        public TokenType Type
        {
            get;
            set;
        }
        public string Value
        {
            get;
            set;
        }
    }

    public class Glyph
    {
        PathGeometry pathGeometry;

        public FillRule FillRule
        {
            get;
            set;
        }

        public string Path
        {
            get;
            set;
        }

        public int Key
        {
            get;
            set;
        }

        public Size Advance
        {
            get;
            set;
        }


        public Thickness Bearings
        {
            get;
            set;
        }

        public PathGeometry PathGeometry
        {
            get
            {
                return CloneGeometry(pathGeometry);
            }
            set
            {
                pathGeometry = value;
            }
        }

        private PathGeometry CloneGeometry(PathGeometry geom)
        {
            PathGeometry pg = new PathGeometry();
            foreach (PathFigure pf in geom.Figures)
            {
                PathFigure pf2 = new PathFigure();
                pf2.StartPoint = pf.StartPoint;
                pf2.IsClosed = pf.IsClosed;
                foreach (PathSegment s in pf.Segments)
                {
                    if (s is LineSegment)
                    {
                        LineSegment ls = s as LineSegment;
                        LineSegment ls2 = SegmentPool.Instance.GetLineSegment();
                        ls2.Point = ls.Point;
                        pf2.Segments.Add(ls2);
                    }
                    else if (s is QuadraticBezierSegment)
                    {
                        QuadraticBezierSegment qs = s as QuadraticBezierSegment;
                        QuadraticBezierSegment qs2 = SegmentPool.Instance.GetQuadraticBezierSegment();
                        qs2.Point1 = qs.Point1;
                        qs2.Point2 = qs.Point2;
                        pf2.Segments.Add(qs2);
                    }
                }
                pg.Figures.Add(pf2);
            }
            return pg;
        }

        private Token GetValueToken(string input, int start, int end)
        {
            Token t = new Token();
            t.Type = TokenType.Value;
            t.Value = input.Substring(start, end - start);
            return t;
        }

        private List<Token> Tokenize(string input)
        {
            input = input.ToLower();
            List<Token> tokens = new List<Token>();
            int prev = 0;
            int offset = 0;
            while (offset < input.Length)
            {
                char c = input[offset];
                if ("0123456789.-".IndexOf(c) >= 0)
                {
                    offset++;
                }
                else
                {
                    Token t = GetValueToken(input, prev, offset);
                    prev = offset;
                    if (t.Value.Length > 0)
                    {
                        tokens.Add(t);
                    }
                    if (c >= 'a')
                    {
                        Token t2 = new Token();
                        t2.Type = TokenType.Command;
                        t2.Value = "" + c;
                        tokens.Add(t2);
                    }
                    offset++;
                    prev = offset;
                }
            }
            return tokens;
        }

        private PathGeometry ParsePath(string input)
        {
            List<Token> tokens = Tokenize(input);
            PathGeometry pg = new PathGeometry();
            PathFigure f = new PathFigure();
            for (int i = 0; i < tokens.Count; i++)
            {
                Token t = tokens[i];
                switch (t.Value)
                {
                    case "f":
                        {
                            i++;
                            if (tokens[i].Value == "1")
                            {
                                FillRule = FillRule.Nonzero;
                            }
                            else
                            {
                                FillRule = FillRule.EvenOdd;
                            }
                        }
                        break;
                    case "l":
                        {
                            LineSegment s = new LineSegment();
                            s.Point = new Point(double.Parse(tokens[i + 1].Value), double.Parse(tokens[i + 2].Value));
                            i += 2;
                            f.Segments.Add(s);
                        }
                        break;
                    case "q":
                        {
                            QuadraticBezierSegment q = new QuadraticBezierSegment();
                            q.Point1 = new Point(double.Parse(tokens[i + 1].Value), double.Parse(tokens[i + 2].Value));
                            q.Point2 = new Point(double.Parse(tokens[i + 3].Value), double.Parse(tokens[i + 4].Value));
                            i += 4;
                            f.Segments.Add(q);
                        }
                        break;
                    case "z":
                        {
                            f.IsClosed = true;
                            pg.Figures.Add(f);
                            f = new PathFigure();
                        }
                        break;
                    case "m":
                        {
                            if (f.Segments.Count > 0)
                            {
                                f.IsClosed = false;
                                pg.Figures.Add(f);
                            }
                            f = new PathFigure();
                            f.StartPoint = new Point(double.Parse(tokens[i + 1].Value), double.Parse(tokens[i + 2].Value));
                            i += 2;
                        }
                        break;
                }
            }
            return pg;
        }

        private Size ParseSize(string input)
        {
            Size sz = new Size();
            string[] s = input.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            sz.Width = double.Parse(s[0]);
            sz.Height = double.Parse(s[1]);
            return sz;
        }

        private Thickness ParseThickness(string input)
        {
            Thickness t = new Thickness();
            string[] s = input.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            t.Left = double.Parse(s[0]);
            t.Top = double.Parse(s[1]);
            t.Right = double.Parse(s[2]);
            t.Bottom = double.Parse(s[3]);
            return t;
        }

        public Glyph(XElement glyphElement)
        {
            Key = int.Parse(glyphElement.Attribute("key").Value);
            Advance = ParseSize(glyphElement.Attribute("advance").Value);
            Bearings = ParseThickness(glyphElement.Attribute("bearings").Value);
            Path = glyphElement.Attribute("path").Value;
            pathGeometry = ParsePath(Path);
        }
    }
}
